import * as path from "path";

import * as Excel from "exceljs";
import * as UnderscoreString from "underscore.string";
const isAlphanumeric = require("is-alphanumeric");
import * as CellValue from "./xlsx-cell-value";
import * as log from "./helpers/log";

export enum ExportTarget {
	All = 0,
	None = 1,
	ClientOnly = 2,
	ServerOnly = 3
}

export type Column = {
	name: string;
	type: CellValue.Type;
	exportTarget: ExportTarget;
	indexNo: number;
	description?: string;
};

export type Sheet = {
	name: string;
	originalFilename: string;
	columns: Column[];
	data: Map<any, any[]>;
};
export const sheets = new Map<string, Sheet>();
const camelize = (str: string): string => {
	str = UnderscoreString.decapitalize(UnderscoreString.camelize(str));

	// ID => Id
	if (str.endsWith(`ID`)) {
		str = str.substr(0, str.length - 1) + "d";
	}

	return str;
};

const importXlsxFile = async (xlsxFile: string) => {
	try {
		const xlsxBasename = path.basename(xlsxFile);
		log.i(`============ ${xlsxBasename} ============`);
		const workbook = new Excel.Workbook();
		await workbook.xlsx.readFile(xlsxFile);

		// 实测以下是同步方法，请放心使用
		workbook.eachSheet((worksheet, sheetId) => {
			const originalSheetName = worksheet.name.trim();

			log.i(`------ ${originalSheetName} ------`);
			if (!originalSheetName || !isAlphanumeric(originalSheetName)) {
				log.e(`${originalSheetName} 不是合法的sheet名。`);
				return;
			}

			const worksheetName = camelize(originalSheetName);
			const columnCount = worksheet.actualColumnCount;
			const rowCount = worksheet.rowCount;
			const sheet: Sheet = {
				name: worksheetName,
				originalFilename: xlsxBasename,
				columns: [null],
				data: new Map()
			};

			log.i(`worksheetName: ${worksheetName}`);
			log.i(`xlsxBasename: ${xlsxBasename}`);
			log.i(`columnCount: ${columnCount}`);
			log.i(`rowCount: ${rowCount}`);

			if (columnCount < 1 || rowCount < 6) {
				return;
			}

			let actualColumnCount: number;
			let rowNo = 0;
			let row: Excel.Row;

			// 列名
			row = worksheet.getRow(++rowNo);

			for (let i = 1; i <= columnCount; i++) {
				let colName = CellValue.asString(row.getCell(i));

				if (1 == i) {
					colName = colName.toLowerCase();

					if ("id" != colName) {
						log.e("不是配置表！");
						return;
					}
				} else {
					// 只有命名规范的列名才视作有效列名，不规范列名按照原样导出
					if (isAlphanumeric(colName)) {
						colName = camelize(colName);
					}
				}

				if (colName.length > 0) {
					sheet.columns.push({
						name: colName,
						type: CellValue.Type.String,
						exportTarget: ExportTarget.None,
						indexNo: 0
					});
				} else {
					break;
				}
			}

			actualColumnCount = sheet.columns.length - 1; // 减1是为了减掉头部的null
			log.i(`actualColumnCount: ${actualColumnCount}`);

			if (actualColumnCount < 1 || "id" != sheet.columns[1].name) {
				log.e("不是配置表！");
				return;
			}

			// 数据类型
			row = worksheet.getRow(++rowNo);

			for (let i = 1; i <= actualColumnCount; i++) {
				sheet.columns[i].type = CellValue.getTypeFromTypeString(CellValue.asString(row.getCell(i)));
			}

			if (CellValue.Type.Number != sheet.columns[1].type && CellValue.Type.String != sheet.columns[1].type) {
				log.e("ID列不是数字或字符串！");
				return;
			}

			// 是否需要导出
			row = worksheet.getRow(++rowNo);

			let exportableColumnFound = false;

			for (let i = 1; i <= actualColumnCount; i++) {
				const columnNameLower = sheet.columns[i].name.toLowerCase();
				let exportTarget: ExportTarget;

				// 当名为name、des等，强制不导出
				// if (
				// 	i > 1 &&
				// 	(columnNameLower.endsWith("name") ||
				// 		"des" == columnNameLower ||
				// 		columnNameLower.endsWith("description"))
				// ) {
				// 	exportTarget = ExportTarget.None;
				// } else {
				exportTarget = <ExportTarget>CellValue.asNumber(row.getCell(i));
				// }

				sheet.columns[i].exportTarget = exportTarget;

				if (ExportTarget.None !== exportTarget) {
					exportableColumnFound = true;
				} else if (1 === i) {
					log.e("ID列不导出，此表无效。");
					return;
				}
			}

			if (!exportableColumnFound) {
				log.e("没有可导出的列。");
				return;
			}

			// 索引列
			row = worksheet.getRow(++rowNo);
			for (let i = 1; i <= actualColumnCount; i++) {
				const indexNo = CellValue.asNumber(row.getCell(i));

				if (ExportTarget.None != sheet.columns[i].exportTarget) {
					sheet.columns[i].indexNo = indexNo;
				}
			}

			// 列描述

			row = worksheet.getRow(++rowNo);

			for (let i = 1; i <= actualColumnCount; i++) {
				sheet.columns[i].description = CellValue.asString(row.getCell(i));
			}

			// 打印列信息

			log.cyan(`列信息：`);
			log.magenta(sheet.columns);

			// 单元格数据

			for (++rowNo; rowNo <= rowCount; rowNo++) {
				row = worksheet.getRow(rowNo);
				let rowData: any[] = new Array(actualColumnCount + 1);
				let rowIsInvaild = false;

				for (let i = 0; i <= actualColumnCount; i++) {
					// 不导出的列，且非中文列，则完全不读取
					if (0 === i || ExportTarget.None == sheet.columns[i].exportTarget) {
						rowData[i] = null;
						continue;
					}

					const cell = row.getCell(i);
					let cellValue;

					switch (sheet.columns[i].type) {
						case CellValue.Type.Json:
							cellValue = CellValue.asJson(cell);
							break;
						case CellValue.Type.Number:
							cellValue = 1 === i ? CellValue.asNullableInt(cell) : CellValue.asNumber(cell);
							break;
						case CellValue.Type.Boolean:
							cellValue = CellValue.asBoolean(cell);
							break;
						case CellValue.Type.NumberArray:
							cellValue = CellValue.asNumberArray(cell);
							break;
						case CellValue.Type.StringArray:
							cellValue = CellValue.asStringArray(cell);
							break;
						case CellValue.Type.BooleanArray:
							cellValue = CellValue.asBooleanArray(cell);
							break;
						case CellValue.Type.NumberArray2D:
							cellValue = CellValue.asNumberArray2D(cell);
							break;
						case CellValue.Type.StringArray2D:
							cellValue = CellValue.asStringArray2D(cell);
							break;
						case CellValue.Type.BooleanArray2D:
							cellValue = CellValue.asBooleanArray2D(cell);
							break;
						default:
							cellValue = CellValue.asString(cell);
							break;
					}

					// 判断行有效性
					if (1 == i) {
						// 数字为空时，视作有效行已经结束
						if (CellValue.Type.Number == sheet.columns[1].type && null === cellValue) {
							rowIsInvaild = true;
							break;
						}

						// 字符串为空时，视作有效行已经结束
						if (CellValue.Type.String == sheet.columns[1].type && 0 === (<string>cellValue).length) {
							rowIsInvaild = true;
							break;
						}
					}

					rowData[i] = cellValue;
				}

				if (rowIsInvaild) {
					log.d("此行无效，本表解析停止。");
					break;
				} else {
					sheet.data.set(rowData[1], rowData);
				}
			}
			sheets.set(sheet.name, sheet);
		});
	} catch (err) {
		log.e(err);
	}
};

export const doImportXlsxFiles = async (xlsxFiles: Set<string>) => {
	try {
		const promises: Promise<void>[] = [];

		for (const xlsxFile of xlsxFiles) {
			promises.push(importXlsxFile(xlsxFile));
		}

		await Promise.all(promises);
	} catch (err) {
		log.e(err);
	}
};
